﻿#include "SrVideoWidget.h"
#include <QDebug>
#include <assert.h>

#include <QTimer>

//准备yuv数据
// ffmpeg -i test.mp4 -t 10 -s 240x128 -pix_fmt yuv420p out240x128.yuv

#define GET_STR(x) #x

//顶点shader
//顶点着色器的源码
//GLSL
//opengl shading language
const char * vString = GET_STR(
            attribute vec4 vertexIn;
        attribute vec2 textureIn;
varying vec2 textureOut;
void main(void)
{
    gl_Position = vertexIn;
    textureOut = textureIn;
}
);

//片元shader
const char *fString = GET_STR(
            varying vec2 textureOut;
        uniform sampler2D tex_y;
uniform sampler2D tex_u;
uniform sampler2D tex_v;
void main(void)
{
    vec3 yuv;
    vec3 rgb;
    yuv.x = texture2D(tex_y, textureOut).r;
    yuv.y = texture2D(tex_u, textureOut).r - 0.5;
    yuv.z = texture2D(tex_v, textureOut).r - 0.5;
    rgb = mat3(1.0, 1.0, 1.0,
               0.0, -0.39465, 2.03211,
               1.13983, -0.58060, 0.0) * yuv;
    gl_FragColor = vec4(rgb, 1.0);
}

);



SrVideoWidget::~SrVideoWidget()
{
    if(datas[0])
    {
        delete [] datas[0];
        datas[0] = NULL;
    }
    if(datas[1])
    {
        delete [] datas[1];
        datas[1] = NULL;
    }
    if(datas[2])
    {
        delete [] datas[2];
        datas[2] = NULL;
    }

    if (texs[0])
        glDeleteTextures(3, texs);


}
SrVideoWidget::SrVideoWidget()
{

}
SrVideoWidget::SrVideoWidget(QWidget *parent) : QOpenGLWidget(parent)
{

    width = 240;
    height = 128;

    datas[0] = NULL;
    datas[1] = NULL;
    datas[2] = NULL;

    texs[0] = NULL;
    texs[1] = NULL;
    texs[2] = NULL;
}

#define ATTRIB_VERTEX 3
#define ATTRIB_TEXTURE 4

void SrVideoWidget::Init(int width, int height)
{
    _mux.lock();
    this->width = width;
    this->height = height;

    if(datas[0])
    {
        delete [] datas[0];
        datas[0] = NULL;
    }
    if(datas[1])
    {
        delete [] datas[1];
        datas[1] = NULL;
    }
    if(datas[2])
    {
        delete [] datas[2];
        datas[2] = NULL;
    }


    datas[0] = new unsigned char[width*height];		// Y
    datas[1] = new unsigned char[width*height/4];	//U
    datas[2] = new unsigned char[width*height/4];	//V


    if (texs[0])
        glDeleteTextures(3, texs);

    //创建材质
    glGenTextures(3, texs);

    //Y
    glBindTexture(GL_TEXTURE_2D, texs[0]);
    //放大过滤，线性插值
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    //创建材质显卡空间
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, 0);

    //U
    glBindTexture(GL_TEXTURE_2D, texs[1]);
    //放大过滤，线性插值
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    //创建材质显卡空间
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width/2, height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);

    //V
    glBindTexture(GL_TEXTURE_2D, texs[2]);
    //放大过滤，线性插值
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    //创建材质显卡空间
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width / 2, height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);

    _mux.unlock();
}
void SrVideoWidget::Repaint(AVFrame *frame)
{
    if (!frame) return;
    _mux.lock();
    // 容错, 保证尺寸正确
    if (!datas[0] || width*height == 0 || frame->width != width || frame->height != height)
    {
        av_frame_free(&frame);
        _mux.unlock();
        return;
    }
    memcpy(datas[0], frame->data[0], width*height);
    memcpy(datas[1], frame->data[1], width*height/4);
    memcpy(datas[2], frame->data[2], width*height/4);

    // 行对齐问题
    _mux.unlock();

    av_frame_free(&frame);
    // 刷新显示
    update();
}
//初始化opengl
void SrVideoWidget::initializeGL()
{
    qDebug() << "initializeGL";
    //初始化QOpenGLFunctions 继承来的数据
    _mux.lock();
    initializeOpenGLFunctions();
    //顶点shader
    bool ret = program.addShaderFromSourceCode(QOpenGLShader::Vertex, vString);
    qDebug() <<"Vertex  " <<ret;
    //片元shader
    ret = program.addShaderFromSourceCode(QOpenGLShader::Fragment, fString);
    qDebug() <<"Fragment  " <<ret;

    //设置顶点坐标的变量， 将顶点绑定到一个位置上, 属性到顶点着色器源码中有声明
    program.bindAttributeLocation("vertexIn",ATTRIB_VERTEX);

    //设置材质标量绑定到一个位置上
    program.bindAttributeLocation("textureIn", ATTRIB_TEXTURE);

    //链接所有添入到的着色器程序
    ret = program.link();
    qDebug() <<"link  " <<ret;
    //激活所有链接
    ret = program.bind();
    qDebug() <<"bind  " <<ret;


    //设置顶点个片元纹理的矩阵坐标

    // 顶点矩阵
    static const GLfloat vertexVertices[] = {
        -1.0f, -1.0f,
        1.0f, -1.0f,
        -1.0f, 1.0f,
        1.0f, 1.0f,
    };
    //纹理矩阵
    static const GLfloat textureVertices[] = {
        0.0f,  1.0f,
        1.0f,  1.0f,
        0.0f,  0.0f,
        1.0f,  0.0f,
    };

    //设置属性ATTRIB_VERTEX的顶点矩阵值以及格式
    glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0,vertexVertices);
    //使其生效
    glEnableVertexAttribArray(ATTRIB_VERTEX);

    //材质
    glVertexAttribPointer(ATTRIB_TEXTURE, 2, GL_FLOAT, 0, 0,textureVertices);
    glEnableVertexAttribArray(ATTRIB_TEXTURE);


    //读取着色器中的数据变量tex_y, tex_u, tex_v的位置,这些变量的声明可以在
    //片段着色器源码中可以看到
    textureUniformY = program.uniformLocation("tex_y");
    textureUniformU = program.uniformLocation("tex_u");
    textureUniformV = program.uniformLocation("tex_v");


    //        ///分配材质内存空间
    //        datas[0] = new unsigned char[width*height];		// Y
    //        datas[1] = new unsigned char[width*height/4];	//U
    //        datas[2] = new unsigned char[width*height/4];	//V


    //        fp = fopen("out240x128.yuv", "rb");
    //        if(NULL == fp)
    //        {
    //            assert(false);
    //        }

    //        //启动定时器
    //        QTimer *ti = new QTimer(this);
    //        connect(ti, SIGNAL(timeout()), this, SLOT(update()));
    //        ti->start(40);

    _mux.unlock();
}
//窗口发生变化
void SrVideoWidget::resizeGL(int w, int h)
{
    qDebug() << "resizeGL w:" <<w <<" h:" <<h;
}
//刷新显示的
void SrVideoWidget::paintGL()
{
    //    if(feof(fp))
    //    {
    //       fseek(fp, 0, SEEK_SET);
    //    }

    //    fread(datas[0], 1, width*height, fp);
    //     fread(datas[1], 1, width*height/4, fp);
    //      fread(datas[2], 1, width*height/4, fp);

    _mux.lock();
    //Y
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texs[0]);

    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, datas[0]);

    glUniform1i(textureUniformY, 0);

    //U
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texs[1]);

    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width/2, height/2, GL_RED, GL_UNSIGNED_BYTE, datas[1]);

    glUniform1i(textureUniformU, 1);


    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, texs[2]);

    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width/2, height/2, GL_RED, GL_UNSIGNED_BYTE, datas[2]);

    glUniform1i(textureUniformV, 2);


    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    qDebug() << "paintGL";
    _mux.unlock();
}
